1. Importar libraries y settings¶
In [1]:
# Import libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
import scipy.cluster.hierarchy as sch
from scipy.spatial import distance
from scipy.stats import chi2_contingency
# Scikit-learn imports
from sklearn.preprocessing import (OrdinalEncoder, LabelBinarizer, OneHotEncoder,
StandardScaler, MinMaxScaler)
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.metrics import (mean_absolute_error, mean_squared_error, r2_score,
silhouette_score, make_scorer)
from sklearn.feature_selection import SelectKBest, chi2
from sklearn.model_selection import (train_test_split, GridSearchCV, learning_curve)
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
# XGBoost import
import xgboost as xgb
# Statsmodels import
import statsmodels.api as sm
# Other imports
from tabulate import tabulate
import joblib
# Adjust the display setting to show all columns
pd.set_option('display.max_columns', None)
# Suppress warnings
warnings.filterwarnings("ignore")
/var/folders/dm/xc68z2ls0d5gs2zbxxn7vn680000gn/T/ipykernel_4380/1844470503.py:2: DeprecationWarning:
Pyarrow will become a required dependency of pandas in the next major release of pandas (pandas 3.0),
(to allow more performant data types, such as the Arrow string type, and better interoperability with other libraries)
but was not found to be installed on your system.
If this would cause problems for you,
please provide us feedback at https://github.com/pandas-dev/pandas/issues/54466
import pandas as pd
In [2]:
seed = 111999
2. Lectura de datos y exploración inicial¶
In [3]:
# Definimos la ruta del archivo CSV que contiene los datos y cargamos el archivo CSV en un DataFrame de pandas.
file_path = '/Users/maria/Desktop/MASTER_DATA_SCIENCE/TFM/Carbon_Emission.csv'
datos_load = pd.read_csv(file_path)
datos = datos_load
display(datos.head())
# Mostramos la información general del DataFrame, incluyendo el tipo de cada variable para verificar el formato y tipo de datos que se ha asignado en la lectura.
display(datos.info())
| Body Type | Sex | Diet | How Often Shower | Heating Energy Source | Transport | Vehicle Type | Social Activity | Monthly Grocery Bill | Frequency of Traveling by Air | Vehicle Monthly Distance Km | Waste Bag Size | Waste Bag Weekly Count | How Long TV PC Daily Hour | How Many New Clothes Monthly | How Long Internet Daily Hour | Energy efficiency | Recycling | Cooking_With | CarbonEmission | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | overweight | female | pescatarian | daily | coal | public | NaN | often | 230 | frequently | 210 | large | 4 | 7 | 26 | 1 | No | ['Metal'] | ['Stove', 'Oven'] | 2238 |
| 1 | obese | female | vegetarian | less frequently | natural gas | walk/bicycle | NaN | often | 114 | rarely | 9 | extra large | 3 | 9 | 38 | 5 | No | ['Metal'] | ['Stove', 'Microwave'] | 1892 |
| 2 | overweight | male | omnivore | more frequently | wood | private | petrol | never | 138 | never | 2472 | small | 1 | 14 | 47 | 6 | Sometimes | ['Metal'] | ['Oven', 'Microwave'] | 2595 |
| 3 | overweight | male | omnivore | twice a day | wood | walk/bicycle | NaN | sometimes | 157 | rarely | 74 | medium | 3 | 20 | 5 | 7 | Sometimes | ['Paper', 'Plastic', 'Glass', 'Metal'] | ['Microwave', 'Grill', 'Airfryer'] | 1074 |
| 4 | obese | female | vegetarian | daily | coal | private | diesel | often | 266 | very frequently | 8457 | large | 1 | 3 | 5 | 6 | Yes | ['Paper'] | ['Oven'] | 4743 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 10000 entries, 0 to 9999 Data columns (total 20 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Body Type 10000 non-null object 1 Sex 10000 non-null object 2 Diet 10000 non-null object 3 How Often Shower 10000 non-null object 4 Heating Energy Source 10000 non-null object 5 Transport 10000 non-null object 6 Vehicle Type 3279 non-null object 7 Social Activity 10000 non-null object 8 Monthly Grocery Bill 10000 non-null int64 9 Frequency of Traveling by Air 10000 non-null object 10 Vehicle Monthly Distance Km 10000 non-null int64 11 Waste Bag Size 10000 non-null object 12 Waste Bag Weekly Count 10000 non-null int64 13 How Long TV PC Daily Hour 10000 non-null int64 14 How Many New Clothes Monthly 10000 non-null int64 15 How Long Internet Daily Hour 10000 non-null int64 16 Energy efficiency 10000 non-null object 17 Recycling 10000 non-null object 18 Cooking_With 10000 non-null object 19 CarbonEmission 10000 non-null int64 dtypes: int64(7), object(13) memory usage: 1.5+ MB
None
In [4]:
# Renombramos columnas específicas del DataFrame para que los nombres sean más claros y consistentes.
datos = datos.rename(columns={'Body Type': 'BodyType',
'How Often Shower': 'ShowerFreq',
'Heating Energy Source': 'EnergyHeat',
'Vehicle Type': 'VehicleType',
'Social Activity': 'SocialActivity',
'Monthly Grocery Bill': 'GroceryBill',
'Frequency of Traveling by Air': 'AirTravelFreq',
'Vehicle Monthly Distance Km': 'DriveDistance',
'Waste Bag Size': 'WasteBagSize',
'Waste Bag Weekly Count': 'WasteBagCount',
'How Long TV PC Daily Hour': 'TVPCHours',
'How Many New Clothes Monthly': 'ClothesNew',
'How Long Internet Daily Hour': 'InternetHours',
'Energy efficiency': 'EnergyEfficiency'
})
In [5]:
datos.head()
Out[5]:
| BodyType | Sex | Diet | ShowerFreq | EnergyHeat | Transport | VehicleType | SocialActivity | GroceryBill | AirTravelFreq | DriveDistance | WasteBagSize | WasteBagCount | TVPCHours | ClothesNew | InternetHours | EnergyEfficiency | Recycling | Cooking_With | CarbonEmission | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | overweight | female | pescatarian | daily | coal | public | NaN | often | 230 | frequently | 210 | large | 4 | 7 | 26 | 1 | No | ['Metal'] | ['Stove', 'Oven'] | 2238 |
| 1 | obese | female | vegetarian | less frequently | natural gas | walk/bicycle | NaN | often | 114 | rarely | 9 | extra large | 3 | 9 | 38 | 5 | No | ['Metal'] | ['Stove', 'Microwave'] | 1892 |
| 2 | overweight | male | omnivore | more frequently | wood | private | petrol | never | 138 | never | 2472 | small | 1 | 14 | 47 | 6 | Sometimes | ['Metal'] | ['Oven', 'Microwave'] | 2595 |
| 3 | overweight | male | omnivore | twice a day | wood | walk/bicycle | NaN | sometimes | 157 | rarely | 74 | medium | 3 | 20 | 5 | 7 | Sometimes | ['Paper', 'Plastic', 'Glass', 'Metal'] | ['Microwave', 'Grill', 'Airfryer'] | 1074 |
| 4 | obese | female | vegetarian | daily | coal | private | diesel | often | 266 | very frequently | 8457 | large | 1 | 3 | 5 | 6 | Yes | ['Paper'] | ['Oven'] | 4743 |
In [6]:
# Genera una lista con los nombres de las variables
variables = list(datos.columns)
# Definimos la variable objetivo 'CarbonEmission' para la predicción y creamos una lista de variables explicativas
VarObj = ['CarbonEmission']
VarExp = [var for var in variables if var != 'CarbonEmission']
# Seleccionamos las columnas numéricas del DataFrame
numericas = datos.select_dtypes(include=['int', 'int32', 'int64','float', 'float32', 'float64']).columns
numericas = numericas[numericas != 'CarbonEmission']
# Seleccionamos las columnas categóricas del DataFrame
categoricas = [variable for variable in VarExp if variable not in numericas]
In [7]:
# Reemplazamos los valores NaN en la columna 'VehicleType' con el string 'NaN'.
# Esto permite tratar los valores faltantes como una categoría específica en lugar de un valor nulo.
datos['VehicleType'] = datos['VehicleType'].fillna('NaN')
2.1 Visualización de la variable objetivo¶
In [8]:
# Creamos una figura y un eje para el histograma
fig, axes = plt.subplots(1,1, figsize=(5, 3))
# Creamos un histograma de la columna 'CarbonEmission' con una estimación de densidad (kde).
sns.histplot(x='CarbonEmission', data=datos, kde=True, ax=axes)
axes.set_title(f'Distribution of CarbonEmission')
axes.set_xlabel('')
axes.set_ylabel('Density')
# Calculamos estadísticas descriptivas para la columna 'CarbonEmission' y aregamos líneas verticales para las estadísticas calculadas en el histograma
med = datos_load['CarbonEmission'].median()
ave = datos_load['CarbonEmission'].mean()
q1 = datos_load['CarbonEmission'].quantile(0.25)
q3 = datos_load['CarbonEmission'].quantile(0.75)
axes.axvline(med, color='r', linestyle='-', label=f'Mediana')
axes.axvline(q1, color='g', linestyle='-', label=f'Q1')
axes.axvline(q3, color='b', linestyle='-', label=f'Q3')
axes.axvline(ave, color='k', linestyle='-', label=f'Media')
# Configuramos el gráfico
handles, labels = axes.get_legend_handles_labels()
fig.legend(handles, labels, loc='upper center', bbox_to_anchor=(0.5, -0.05), ncol=4)
plt.title('Distribución de la Huella de Carbono')
plt.xlabel('Emisión de Carbono')
plt.ylabel('Frecuencia')
plt.grid(True)
plt.show()
2.2 Visualización de las variables explicativas categóricas¶
In [9]:
# Identificamos las columnas categóricas en el DataFrame
categorical_columns = datos.select_dtypes(include=['object']).columns
In [10]:
# Primero se investiga la distribución de la variable objetivo en relación a las categorías de cada variable categórica
# Configuramos el número de subplots, uno para cada variable categórica
fig, axes = plt.subplots(len(categorical_columns), 1, figsize=(7, 5 * len(categorical_columns)))
# Iteramos sobre cada variable categórica para crear un histograma
for idx, column in enumerate(categorical_columns):
# Calculamos la media de 'CarbonEmission' para cada categoría de la variable categórica.
mean_emission = datos.groupby(column)['CarbonEmission'].mean()
unique_values = mean_emission.index
# Configuramos el gráfico
palette = sns.color_palette("viridis", len(unique_values))
ax = axes[idx]
for i, value in enumerate(unique_values):
subset_df = datos[datos[column] == value]
# Creamos un histograma con densidad (kde) para la categoría actual
sns.histplot(data=subset_df, x="CarbonEmission", color=palette[i], kde=True, label=value, alpha=0.6, ax=ax)
ax.set_title(f"Distribución de la Huella de Carbono por {column}")
ax.legend(title=column)
plt.tight_layout()
plt.show()
In [11]:
# A continuación, se investiga la media de la variable objetivo en relación a las categorías de cada variable categórica
# Crear los subplots, uno para cada variable categórica.
fig, axes = plt.subplots(len(categorical_columns), 1, figsize=(7, 5 * len(categorical_columns)))
# Iteramos sobre cada variable categórica para crear un gráfico de barras
for idx, column in enumerate(categorical_columns):
ax = axes[idx]
# Calculamos la media de 'CarbonEmission' para cada categoría de la variable categórica
mean_emission = datos.groupby(column)['CarbonEmission'].mean()
# Configuramos el gráfico
sns.barplot(x=mean_emission.index, y=mean_emission.values, ax=ax, palette="viridis")
ax.set_title(f"Huella de Carbono media por {column}")
ax.set_xlabel(column)
ax.set_ylabel("Huella de Carbono media")
plt.tight_layout()
plt.show()
In [12]:
# Por último investigamos las distribuciones de cada categoría por separado para poder observar los comportamientos individuales mejor
# Iteramos sobre cada variable categórica para analizar la distribución de la huella de carbono por categorías
for column in categorical_columns:
# Creamos un FacetGrid para visualizar la distribución de la huella de carbono para cada categoría de la variable
g = sns.FacetGrid(datos, col=column, col_wrap=3, height=4, palette="Set3")
# Configuramos el gráfico
g.map(sns.kdeplot, "CarbonEmission", fill=True)
g.fig.suptitle(f"Distribución de la Huella de Carbono por {column}", y=1.02)
plt.tight_layout()
plt.show()
Se investigan más detalladamente algunas de las variables
In [13]:
unique_Social_Activity = datos['BodyType'].unique()
# Configuramos la figura y los subplots para visualizar la distribución de huella de carbono
palette = sns.color_palette("viridis", len(unique_Social_Activity))
fig, axes = plt.subplots(1, 4, figsize=(15, 5))
for i, activity in enumerate(unique_Social_Activity):
ax = axes[i]
activity_df = datos[datos['BodyType'] == activity]
sns.histplot(data=activity_df, x="CarbonEmission", color=palette[i], ax=ax, kde=True)
ax.set_title(f"Distribution for {activity}")
plt.tight_layout()
plt.show()
In [14]:
unique_Social_Activity = datos['Diet'].unique()
# Configuramos la figura y los subplots para visualizar la distribución de huella de carbono
palette = sns.color_palette("viridis", len(unique_Social_Activity))
fig, axes = plt.subplots(1, 4, figsize=(15, 5))
for i, activity in enumerate(unique_Social_Activity):
ax = axes[i]
activity_df = datos[datos['Diet'] == activity]
sns.histplot(data=activity_df, x="CarbonEmission", color=palette[i], ax=ax, kde=True)
ax.set_title(f"Distribution for {activity}")
plt.tight_layout()
plt.show()
In [15]:
unique_Social_Activity = datos['SocialActivity'].unique()
# Configuramos la figura y los subplots para visualizar la distribución de huella de carbono
palette = sns.color_palette("viridis", len(unique_Social_Activity))
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
for i, activity in enumerate(unique_Social_Activity):
ax = axes[i]
activity_df = datos[datos['SocialActivity'] == activity]
sns.histplot(data=activity_df, x="CarbonEmission", color=palette[i], ax=ax, kde=True)
ax.set_title(f"Distribution for {activity} Activity")
plt.tight_layout()
plt.show()
Investigación de las variables categóricas Transport y VehicleType
In [16]:
# Creamos una nueva columna 'Vehicle Type Missing' para indicar si el valor en 'VehicleType' es 'NaN' o no
datos['Vehicle Type Missing'] = datos['VehicleType'].apply(lambda x: 'NaN' if x == 'NaN' else 'Not NaN')
# Creamos un gráfico de conteo para visualizar la distribución de la variable 'Transport'
plt.figure(figsize=(7, 4))
sns.countplot(data=datos, x='Transport', hue='Vehicle Type Missing', palette='viridis')
# Configuramos el gráfico
plt.title('Distribución de variable Transport en función de valores NaN en VehicleType')
plt.xlabel('Transport')
plt.ylabel('Count')
plt.xticks(rotation=0)
plt.tight_layout()
plt.show()
In [17]:
# Agrupamos los datos por 'Transport' y 'Vehicle Type Missing' para obtener los conteos
transport_vehicle_counts = datos.groupby(['Transport', 'Vehicle Type Missing']).size().reset_index(name='Count')
# Pivotamos la tabla para visualizar mejor los conteos de 'NaN' y 'Not NaN' para cada tipo de transporte
pivot_table = transport_vehicle_counts.pivot(index='Transport', columns='Vehicle Type Missing', values='Count').fillna(0)
print(pivot_table)
Vehicle Type Missing NaN Not NaN Transport private 0.0 3279.0 public 3294.0 0.0 walk/bicycle 3427.0 0.0
In [18]:
datos = datos.drop(columns=['Vehicle Type Missing'])
2.3 Visualización de las variables explicativas numéricas¶
In [19]:
# Iteramos sobre cada variable numérica en el DataFrame para crear gráficos individuales de las distribuciones de las variables explicativas numéricas
for numerica in numericas:
# Creamos una figura con tres subplots dispuestos en una fila
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18, 4))
# Histograma: muestra la distribución de la variable numérica.
sns.histplot(data=datos, x=datos[numerica], ax=ax1)
ax1.set_title(f"Histograma de {numerica}")
# Boxplot: muestra la distribución y los valores atípicos de la variable numérica.
sns.boxplot(data=datos, x=datos[numerica], ax=ax2)
ax2.set_title(f"Boxplot de {numerica}")
# Diagrama de dispersión: muestra la relación entre la variable numérica y 'CarbonEmission'.
sns.scatterplot(data=datos, x=datos[numerica], y='CarbonEmission', ax=ax3)
ax3.set_title(f"Dispersión de {numerica} vs CarbonEmission")
plt.show()
In [20]:
# Configuramos el tamaño de la figura para el gráfico
plt.figure(figsize=(7, 5))
# Creamos un gráfico de dispersión (scatter plot) para visualizar la relación entre 'DriveDistance' y 'CarbonEmission'.
# 'hue' se usa para distinguir entre diferentes tipos de vehículos ('VehicleType') con diferentes colores.
sns.scatterplot(data=datos, x="DriveDistance", y="CarbonEmission", hue="VehicleType", palette="viridis", alpha=0.6)
# Configuramos el gráfico
plt.title("Vehicle Monthly Distance Km vs. Carbon Emission")
plt.xlabel("Vehicle Monthly Distance Km")
plt.ylabel("Carbon Emission")
plt.tight_layout()
plt.show()
In [21]:
# Configuramos el tamaño de la figura para el gráfico
plt.figure(figsize=(7, 5))
# Creamos un gráfico de dispersión (scatter plot) para visualizar la relación entre 'DriveDistance' y 'CarbonEmission'.
# 'hue' se usa para distinguir entre diferentes tipos de vehículos ('AirTravelFreq') con diferentes colores.
hue_order = ["never", "rarely", "frequently", "very frequently"]
sns.scatterplot(data=datos, x="DriveDistance", y="CarbonEmission", hue="AirTravelFreq", hue_order=hue_order, palette="viridis", alpha=0.6)
# Configuramos el gráfico
plt.title("Vehicle Monthly Distance Km vs. Carbon Emission")
plt.xlabel("Vehicle Monthly Distance Km")
plt.ylabel("Carbon Emission")
plt.tight_layout()
plt.show()
In [22]:
# Creamos un gráfico de líneas para visualizar la relación entre 'DriveDistance' y 'CarbonEmission'
# Usamos `sns.lineplot` para trazar una línea que representa cómo cambia la huella de carbono en función de la distancia recorrida
sns.lineplot(data=datos, x = "DriveDistance", y="CarbonEmission")
# Configuramos el gráfico
plt.ylabel("Carbon Emission")
plt.xlabel("Distancia recorrida en kilómetros")
plt.title("Huella de Carbono en relación a distancia recorrida")
plt.show()
In [23]:
# Creamos un gráfico de regresión para visualizar la relación entre 'DriveDistance' y 'CarbonEmission'.
# Utilizamos `sns.regplot` para trazar tanto los puntos de datos como una línea de ajuste (regresión).
sns.regplot(data=datos, x="DriveDistance", y="CarbonEmission", scatter_kws={'s':10}, line_kws={'color':'red'})
# Configuramos el gráfico
plt.ylabel("Carbon Emission")
plt.xlabel("Distancia recorrida en kilómetros")
plt.title("Huella de Carbono en relación a distancia recorrida")
plt.show()
In [24]:
# Creamos un gráfico de líneas para visualizar la relación entre 'ClothesNew' y 'CarbonEmission'
# Usamos `sns.lineplot` para trazar una línea que representa cómo cambia la huella de carbono en función de la distancia recorrida
sns.lineplot(data=datos, x = "ClothesNew", y="CarbonEmission")
# Configuramos el gráfico
plt.ylabel("Carbon Emission")
plt.xlabel("Número de prendas compradas mensualmente")
plt.title("Huella de Carbono en relación a la compra de prendas nuevas")
plt.show()
In [25]:
# Creamos un gráfico de regresión para visualizar la relación entre 'DriveDistance' y 'CarbonEmission'.
# Utilizamos `sns.regplot` para trazar tanto los puntos de datos como una línea de ajuste (regresión).
sns.regplot(data=datos, x="ClothesNew", y="CarbonEmission", scatter_kws={'s':10}, line_kws={'color':'red'})
# Configuramos el gráfico
plt.ylabel("Carbon Emission")
plt.xlabel("Número de prendas compradas mensualmente")
plt.title("Huella de Carbono en relación a la compra de prendas nuevas")
plt.show()
3. Preparación de las variables¶
3.1 Variables independientes a combinar¶
In [26]:
# Definimos una nueva columna 'TransportVehicleType' que inicialmente toma los valores de la columna 'VehicleType'
datos["TransportVehicleType"]=datos["VehicleType"]
# Reemplazamos los valores 'NaN' en 'TransportVehicleType' con los valores correspondientes de la columna 'Transport' y eliminamos las columnas originales
datos.loc[datos["TransportVehicleType"] == 'NaN', "TransportVehicleType"] = datos["Transport"]
datos = datos.drop(columns=['VehicleType', 'Transport'])
3.2 Estandarización de variables numéricas¶
In [27]:
# Estandarizar las columnas numéricas utilizando StandardScaler.
datos_stn = pd.DataFrame(
StandardScaler().fit_transform(datos[numericas]), # Datos estandarizados
columns=['{}_z'.format(variable) for variable in numericas], # Nombres de columnas estandarizadas
index=datos[numericas].index # Índices (etiquetas de filas) del DataFrame
)
# Eliminar las columnas originales del DataFrame 'datos' que han sido estandarizadas e incluir las nuevas
datos_sin_numericas = datos.drop(columns=numericas)
datos = pd.concat([datos_sin_numericas, datos_stn], axis=1)
# Mostrar las primeras filas del DataFrame final para verificar
datos.head()
Out[27]:
| BodyType | Sex | Diet | ShowerFreq | EnergyHeat | SocialActivity | AirTravelFreq | WasteBagSize | EnergyEfficiency | Recycling | Cooking_With | CarbonEmission | TransportVehicleType | GroceryBill_z | DriveDistance_z | WasteBagCount_z | TVPCHours_z | ClothesNew_z | InternetHours_z | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | overweight | female | pescatarian | daily | coal | often | frequently | large | No | ['Metal'] | ['Stove', 'Oven'] | 2238 | public | 0.777025 | -0.657677 | -0.012360 | -0.723218 | 0.060621 | -1.496402 |
| 1 | obese | female | vegetarian | less frequently | natural gas | often | rarely | extra large | No | ['Metal'] | ['Stove', 'Microwave'] | 1892 | walk/bicycle | -0.828947 | -0.730251 | -0.514803 | -0.441767 | 0.877059 | -0.946714 |
| 2 | overweight | male | omnivore | more frequently | wood | never | never | small | Sometimes | ['Metal'] | ['Oven', 'Microwave'] | 2595 | petrol | -0.496677 | 0.159055 | -1.519689 | 0.261863 | 1.489387 | -0.809292 |
| 3 | overweight | male | omnivore | twice a day | wood | sometimes | rarely | medium | Sometimes | ['Paper', 'Plastic', 'Glass', 'Metal'] | ['Microwave', 'Grill', 'Airfryer'] | 1074 | walk/bicycle | -0.233630 | -0.706782 | -0.514803 | 1.106218 | -1.368146 | -0.671870 |
| 4 | obese | female | vegetarian | daily | coal | often | very frequently | large | Yes | ['Paper'] | ['Oven'] | 4743 | diesel | 1.275430 | 2.320034 | -1.519689 | -1.286122 | -1.368146 | -0.809292 |
3.3 Variables independientes con posibles combinaciones de respuestas¶
In [28]:
# Manejo de la columna 'Recycling'
# Creamos un conjunto con los valores únicos posibles en la columna 'Recycling'
# Asignamos 1 si el valor está presente en la columna original, de lo contrario, asignamos 0
unique_values_Recycling= set([item for sublist in datos['Recycling'].unique() for item in eval(sublist)])
for item in unique_values_Recycling:
datos['Recycling'+str(item)] = datos['Recycling'].apply(lambda x: 1 if item in x else 0)
# Manejo de la columna 'Cooking_with'
# Creamos un conjunto con los valores únicos posibles en la columna 'Recycling'
# Asignamos 1 si el valor está presente en la columna original, de lo contrario, asignamos 0
unique_values_cooking_With= set([item for sublist in datos['Cooking_With'].unique() for item in eval(sublist)])
for item in unique_values_cooking_With:
datos['CookingWith'+str(item)] = datos['Cooking_With'].apply(lambda x: 1 if item in x else 0)
# Eliminamos las columnas originales 'Recycling' y 'Cooking_With' ya que hemos creado nuevas columnas binarias
datos = datos.drop(columns=['Recycling', 'Cooking_With'])
3.4 Codificación de variables independientes con una sola respuesta¶
In [29]:
# Definimos los codificadores para las columnas
encoders = {
"BodyType": OrdinalEncoder(categories=[['underweight', 'normal', 'overweight', 'obese']]),
"Sex": LabelBinarizer(),
"ShowerFreq": OrdinalEncoder(categories=[['less frequently', 'more frequently', 'daily', 'twice a day']]),
"EnergyHeat": OneHotEncoder(categories=[['electricity', 'natural gas', 'coal', 'wood']], sparse_output=False),
"TransportVehicleType": OneHotEncoder(categories=[['walk/bicycle', 'public','electric', 'hybrid', 'lpg' ,'petrol','diesel']], sparse_output=False),
"SocialActivity": OrdinalEncoder(categories=[['never', 'sometimes', 'often']]),
"AirTravelFreq": OrdinalEncoder(categories=[['never', 'rarely', 'frequently', 'very frequently']]),
"WasteBagSize": OrdinalEncoder(categories=[['small', 'medium', 'large', 'extra large']]),
"EnergyEfficiency": OrdinalEncoder(categories=[['No','Sometimes','Yes']]),
"Diet": OneHotEncoder(categories=[['pescatarian', 'vegetarian', 'omnivore', 'vegan']], sparse_output=False)
}
# Aplicamos las transformaciones en las columnas correspondientes
for column, encoder in encoders.items():
print(column)
print(encoder)
print(' ')
# Para columnas con OneHotEncoder
if column == 'EnergyHeat' or column == 'TransportVehicleType' or column == 'Diet':
# Aplicamos la codificación OneHotEncoder
encoded_data = encoder.fit_transform(datos[column].array.reshape(-1, 1))
encoded_columns = encoder.get_feature_names_out([column])
# Creamos un DataFrame con las columnas codificadas y lo unimos al original
onehot_df = pd.DataFrame(encoded_data,
index = datos.index,
columns=encoded_columns)
onehot_df = onehot_df.astype(int)
datos = datos.join(onehot_df)
datos = datos.drop(columns=[column])
# Para columnas con OrdinalEncoder y LabelBinarizer
else:
datos[column] = encoder.fit_transform(datos[column].array.reshape(-1, 1))
# Verificamos las primeras filas del DataFrame transformado
datos.head(5)
BodyType
OrdinalEncoder(categories=[['underweight', 'normal', 'overweight', 'obese']])
Sex
LabelBinarizer()
ShowerFreq
OrdinalEncoder(categories=[['less frequently', 'more frequently', 'daily',
'twice a day']])
EnergyHeat
OneHotEncoder(categories=[['electricity', 'natural gas', 'coal', 'wood']],
sparse_output=False)
TransportVehicleType
OneHotEncoder(categories=[['walk/bicycle', 'public', 'electric', 'hybrid',
'lpg', 'petrol', 'diesel']],
sparse_output=False)
SocialActivity
OrdinalEncoder(categories=[['never', 'sometimes', 'often']])
AirTravelFreq
OrdinalEncoder(categories=[['never', 'rarely', 'frequently',
'very frequently']])
WasteBagSize
OrdinalEncoder(categories=[['small', 'medium', 'large', 'extra large']])
EnergyEfficiency
OrdinalEncoder(categories=[['No', 'Sometimes', 'Yes']])
Diet
OneHotEncoder(categories=[['pescatarian', 'vegetarian', 'omnivore', 'vegan']],
sparse_output=False)
Out[29]:
| BodyType | Sex | ShowerFreq | SocialActivity | AirTravelFreq | WasteBagSize | EnergyEfficiency | CarbonEmission | GroceryBill_z | DriveDistance_z | WasteBagCount_z | TVPCHours_z | ClothesNew_z | InternetHours_z | RecyclingMetal | RecyclingPlastic | RecyclingPaper | RecyclingGlass | CookingWithGrill | CookingWithOven | CookingWithMicrowave | CookingWithAirfryer | CookingWithStove | EnergyHeat_electricity | EnergyHeat_natural gas | EnergyHeat_coal | EnergyHeat_wood | TransportVehicleType_walk/bicycle | TransportVehicleType_public | TransportVehicleType_electric | TransportVehicleType_hybrid | TransportVehicleType_lpg | TransportVehicleType_petrol | TransportVehicleType_diesel | Diet_pescatarian | Diet_vegetarian | Diet_omnivore | Diet_vegan | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2.0 | 0 | 2.0 | 2.0 | 2.0 | 2.0 | 0.0 | 2238 | 0.777025 | -0.657677 | -0.012360 | -0.723218 | 0.060621 | -1.496402 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| 1 | 3.0 | 0 | 0.0 | 2.0 | 1.0 | 3.0 | 0.0 | 1892 | -0.828947 | -0.730251 | -0.514803 | -0.441767 | 0.877059 | -0.946714 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| 2 | 2.0 | 1 | 1.0 | 0.0 | 0.0 | 0.0 | 1.0 | 2595 | -0.496677 | 0.159055 | -1.519689 | 0.261863 | 1.489387 | -0.809292 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
| 3 | 2.0 | 1 | 3.0 | 1.0 | 1.0 | 1.0 | 1.0 | 1074 | -0.233630 | -0.706782 | -0.514803 | 1.106218 | -1.368146 | -0.671870 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| 4 | 3.0 | 0 | 2.0 | 2.0 | 3.0 | 2.0 | 2.0 | 4743 | 1.275430 | 2.320034 | -1.519689 | -1.286122 | -1.368146 | -0.809292 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 |
3.5 Comprobación de los datos¶
In [30]:
# Generamos una lista con los nombres de todas las variables del DataFrame, y se distingue la variable objetivo de las explicativas
variables = list(datos.columns)
VarObj = ['CarbonEmission']
VarExp = [var for var in variables if var != 'CarbonEmission']
In [31]:
# Valores duplicados
duplicados = datos.duplicated().sum()
print(f"Número de filas duplicadas: {duplicados}")
# Valores faltantes
print(datos.isnull().sum())
# Verificación tipos de datos
print(datos.dtypes)
# Valores atípicos
sns.boxplot(data=datos[VarExp])
plt.xticks(rotation=90)
plt.show()
Número de filas duplicadas: 0 BodyType 0 Sex 0 ShowerFreq 0 SocialActivity 0 AirTravelFreq 0 WasteBagSize 0 EnergyEfficiency 0 CarbonEmission 0 GroceryBill_z 0 DriveDistance_z 0 WasteBagCount_z 0 TVPCHours_z 0 ClothesNew_z 0 InternetHours_z 0 RecyclingMetal 0 RecyclingPlastic 0 RecyclingPaper 0 RecyclingGlass 0 CookingWithGrill 0 CookingWithOven 0 CookingWithMicrowave 0 CookingWithAirfryer 0 CookingWithStove 0 EnergyHeat_electricity 0 EnergyHeat_natural gas 0 EnergyHeat_coal 0 EnergyHeat_wood 0 TransportVehicleType_walk/bicycle 0 TransportVehicleType_public 0 TransportVehicleType_electric 0 TransportVehicleType_hybrid 0 TransportVehicleType_lpg 0 TransportVehicleType_petrol 0 TransportVehicleType_diesel 0 Diet_pescatarian 0 Diet_vegetarian 0 Diet_omnivore 0 Diet_vegan 0 dtype: int64 BodyType float64 Sex int64 ShowerFreq float64 SocialActivity float64 AirTravelFreq float64 WasteBagSize float64 EnergyEfficiency float64 CarbonEmission int64 GroceryBill_z float64 DriveDistance_z float64 WasteBagCount_z float64 TVPCHours_z float64 ClothesNew_z float64 InternetHours_z float64 RecyclingMetal int64 RecyclingPlastic int64 RecyclingPaper int64 RecyclingGlass int64 CookingWithGrill int64 CookingWithOven int64 CookingWithMicrowave int64 CookingWithAirfryer int64 CookingWithStove int64 EnergyHeat_electricity int64 EnergyHeat_natural gas int64 EnergyHeat_coal int64 EnergyHeat_wood int64 TransportVehicleType_walk/bicycle int64 TransportVehicleType_public int64 TransportVehicleType_electric int64 TransportVehicleType_hybrid int64 TransportVehicleType_lpg int64 TransportVehicleType_petrol int64 TransportVehicleType_diesel int64 Diet_pescatarian int64 Diet_vegetarian int64 Diet_omnivore int64 Diet_vegan int64 dtype: object
In [32]:
# Calcular el número de valores únicos por columna
distinct_values = datos.nunique()
print(distinct_values)
print(datos.dtypes)
BodyType 4 Sex 2 ShowerFreq 4 SocialActivity 3 AirTravelFreq 4 WasteBagSize 4 EnergyEfficiency 3 CarbonEmission 3509 GroceryBill_z 250 DriveDistance_z 4003 WasteBagCount_z 7 TVPCHours_z 25 ClothesNew_z 51 InternetHours_z 25 RecyclingMetal 2 RecyclingPlastic 2 RecyclingPaper 2 RecyclingGlass 2 CookingWithGrill 2 CookingWithOven 2 CookingWithMicrowave 2 CookingWithAirfryer 2 CookingWithStove 2 EnergyHeat_electricity 2 EnergyHeat_natural gas 2 EnergyHeat_coal 2 EnergyHeat_wood 2 TransportVehicleType_walk/bicycle 2 TransportVehicleType_public 2 TransportVehicleType_electric 2 TransportVehicleType_hybrid 2 TransportVehicleType_lpg 2 TransportVehicleType_petrol 2 TransportVehicleType_diesel 2 Diet_pescatarian 2 Diet_vegetarian 2 Diet_omnivore 2 Diet_vegan 2 dtype: int64 BodyType float64 Sex int64 ShowerFreq float64 SocialActivity float64 AirTravelFreq float64 WasteBagSize float64 EnergyEfficiency float64 CarbonEmission int64 GroceryBill_z float64 DriveDistance_z float64 WasteBagCount_z float64 TVPCHours_z float64 ClothesNew_z float64 InternetHours_z float64 RecyclingMetal int64 RecyclingPlastic int64 RecyclingPaper int64 RecyclingGlass int64 CookingWithGrill int64 CookingWithOven int64 CookingWithMicrowave int64 CookingWithAirfryer int64 CookingWithStove int64 EnergyHeat_electricity int64 EnergyHeat_natural gas int64 EnergyHeat_coal int64 EnergyHeat_wood int64 TransportVehicleType_walk/bicycle int64 TransportVehicleType_public int64 TransportVehicleType_electric int64 TransportVehicleType_hybrid int64 TransportVehicleType_lpg int64 TransportVehicleType_petrol int64 TransportVehicleType_diesel int64 Diet_pescatarian int64 Diet_vegetarian int64 Diet_omnivore int64 Diet_vegan int64 dtype: object
4. Relaciones entre variables¶
4.1 Correlaciones entre variables¶
In [33]:
# Obtener la lista de columnas
columns = datos.columns.tolist()
# Mover 'CarbonEmission' al final de la lista
columns.append(columns.pop(columns.index('CarbonEmission')))
# Reordenar el DataFrame con la columna 'CarbonEmission' al final
datos = datos[columns]
In [34]:
# Calcular la matriz de correlación entre todas las variables
corr_matrix = datos.corr()
# Configurar el gráfico para que solo se muestren anotaciones en los valores de correlación > 0.5
# Esto resalta únicamente las correlaciones fuertes y deja las débiles sin anotaciones
rounded_corr_matrix = corr_matrix.round(2)
annot_matrix = rounded_corr_matrix.copy()
annot_matrix = annot_matrix.where(corr_matrix.abs() > 0.5)
# Reemplazar los NaN por una cadena vacía para evitar que se muestren en el heatmap
annot_matrix = annot_matrix.fillna('')
# Congfigurar el gráfico
plt.figure(figsize=(15, 10))
sns.heatmap(rounded_corr_matrix, cmap='coolwarm', annot=annot_matrix, fmt="")
plt.show()
4.2 Relaciones entre variables con V de Cramer y Chi Cuadrado¶
Para cuantificar las relaciones entre las variables del conjunto de datos, se utilizará el test de Chicuadrado (χ²). Este test permite evaluar la existencia de una asociación estadísticamente significativa entre dos variables. Adicionalmente, se calculará el coeficiente V de Cramer para cada variable original con la variable objetivo correspondiente
In [35]:
def Vcramer(v, target):
"""
Calcula el coeficiente V de Cramer entre dos variables. Si alguna de ellas es continua, la discretiza.
Datos de entrada:
- v: Serie de datos categóricos o cuantitativos.
- target: Serie de datos categóricos o cuantitativos.
Datos de salida:
- Coeficiente V de Cramer que mide la asociación entre las dos variables.
"""
if v.dtype == 'float64' or v.dtype == 'int64' and v.nunique() > 2:
# Si v es numérica, la discretiza en intervalos y rellena los valores faltantes
p = sorted(list(set(v.quantile([0, 0.2, 0.4, 0.6, 0.8, 1.0]))))
v = pd.cut(v, bins=p)
v = v.fillna(v.min())
if target.dtype == 'float64' or target.dtype == 'int64' and v.nunique() > 2:
# Si target es numérica, la discretiza en intervalos y rellena los valores faltantes
p = sorted(list(set(target.quantile([0, 0.2, 0.4, 0.6, 0.8, 1.0]))))
target = pd.cut(target, bins=p)
target = target.fillna(target.min())
# Calcula una tabla de contingencia entre v y target
tabla_cruzada = pd.crosstab(v, target)
# Calcula el chi-cuadrado y el coeficiente V de Cramer
chi2 = chi2_contingency(tabla_cruzada)[0]
n = tabla_cruzada.sum().sum()
v_cramer = np.sqrt(chi2 / (n * (min(tabla_cruzada.shape) - 1)))
return v_cramer
In [36]:
def graficoVcramer(matriz, target):
"""
Genera un gráfico de barras horizontales que muestra el coeficiente V de Cramer entre cada columna de matriz y la variable target.
Datos de entrada:
- matriz: DataFrame con las variables a comparar.
- target: Serie de la variable objetivo (categórica).
Datos de salida:
"""
# Calcula el coeficiente V de Cramer para cada columna de matriz y target
salidaVcramer = {x: Vcramer(matriz[x].squeeze(), target.squeeze()) for x in matriz.columns}
print(salidaVcramer)
# Ordena los resultados en orden descendente por el coeficiente V de Cramer
sorted_data = dict(sorted(salidaVcramer.items(), key=lambda item: item[1], reverse=True))
# Crea el gráfico de barras horizontales
plt.figure(figsize=(20, 12))
plt.barh(list(sorted_data.keys()), list(sorted_data.values()), color='skyblue')
plt.xlabel('V de Cramer')
plt.show()
In [37]:
# Relación de cada variable de entrada con la variable objetivo, permiter observar la importancia de las variables
graficoVcramer(datos[VarExp], datos[VarObj])
{'BodyType': 0.14235250519587506, 'Sex': 0.5952491369191636, 'ShowerFreq': 0.025460320770804543, 'SocialActivity': 0.5941663976549145, 'AirTravelFreq': 0.43123541993360337, 'WasteBagSize': 0.11061767497617049, 'EnergyEfficiency': 0.5849467675545326, 'GroceryBill_z': 0.047985542202580105, 'DriveDistance_z': 0.27408468227932015, 'WasteBagCount_z': 0.095934491216027, 'TVPCHours_z': 0.017513963579432454, 'ClothesNew_z': 0.12164784881289023, 'InternetHours_z': 0.037022929112716076, 'RecyclingMetal': 0.5918629619372979, 'RecyclingPlastic': 0.5931716001459423, 'RecyclingPaper': 0.5859205205701488, 'RecyclingGlass': 0.5959134007082039, 'CookingWithGrill': 0.5905539447212004, 'CookingWithOven': 0.5873484303048224, 'CookingWithMicrowave': 0.593123531760101, 'CookingWithAirfryer': 0.5905539447212004, 'CookingWithStove': 0.5975519707004231, 'EnergyHeat_electricity': 0.6079213859135615, 'EnergyHeat_natural gas': 0.581977213643523, 'EnergyHeat_coal': 0.6166242692287052, 'EnergyHeat_wood': 0.5868588126410205, 'TransportVehicleType_walk/bicycle': 0.5930255271328096, 'TransportVehicleType_public': 0.5674723353503216, 'TransportVehicleType_electric': 0.5443554038148077, 'TransportVehicleType_hybrid': 0.6393783612689266, 'TransportVehicleType_lpg': 0.7389779410911212, 'TransportVehicleType_petrol': 0.7958663517842256, 'TransportVehicleType_diesel': 0.7216232333844254, 'Diet_pescatarian': 0.5786724012730501, 'Diet_vegetarian': 0.5899353012959806, 'Diet_omnivore': 0.6006101681766327, 'Diet_vegan': 0.5835581333193769}
In [38]:
# Crear una DataFrame para almacenar los resultados del coeficiente V de Cramer
V = [[variable, VarObj, Vcramer(datos[variable].squeeze(), datos[VarObj].squeeze())] for variable in variables]
VCramer_Dataframe = pd.DataFrame(V, columns=['Variable', 'Objetivo', 'Vcramer'])
# Relación entre las variables explicativas y la variabje objetivo
matriz_p_obj = pd.DataFrame(index=datos.columns, columns=VarObj)
matriz_p_asociacion_obj = pd.DataFrame(index=datos.columns, columns=VarObj)
# Iterar sobre todas las columnas de datos_muestra_imputados
for var1 in datos.columns:
# Tabla cruzada entre la variable de datos_muestra_imputados y varObj
tabla_cruzada = pd.crosstab(datos[var1], VarObj)
# Realizar la prueba de Chi-cuadrado
chi2val, p, dof, expected = chi2_contingency(tabla_cruzada)
# Guardar el valor de p en la matriz
matriz_p_obj.loc[var1, VarObj] = p
matriz_p_asociacion_obj.loc[var1, VarObj] = 0 if p >= 0.05 else 1
In [39]:
# Relación entre las variables explicativas (dos a dos)
matriz_p = pd.DataFrame(index=datos.columns, columns=datos.columns)
matriz_p_asociacion = pd.DataFrame(index=datos.columns, columns=datos.columns)
# Iterar sobre todas las combinaciones posibles de variables
for var1 in datos.columns:
for var2 in datos.columns:
# Tabla cruzada entre las dos variables
tabla_cruzada = pd.crosstab(datos[var1], datos[var2])
# Realizar la prueba de Chi-cuadrado
chi2val, p, dof, expected = chi2_contingency(tabla_cruzada)
# Guardar el valor de p en la matriz
matriz_p.loc[var1, var2] = p
matriz_p_asociacion.loc[var1, var2] = 0 if p >= 0.05 else 1
# Correlación entre variables numéricas
corr_numericas = datos.corr()
# print(corr_numericas)
5. Análisis de Componentes Principales¶
In [40]:
# Crea una instancia de Análisis de Componentes Principales (ACP):
# - Utilizamos PCA(n_components=4) para crear un objeto PCA que realizará un análisis de componentes principales.
# - Establecemos n_components en 4 para retener el maximo de las componentes principales (maximo= numero de variables).
pca_4 = PCA(n_components=4)
# Aplicar el Análisis de Componentes Principales (ACP) a los datos estandarizados:
# - Usamos pca.fit(notas_estandarizadas) para ajustar el modelo de ACP a los datos estandarizados.
datos_selected = datos[VarExp]
fit_4 = pca_4.fit(datos_selected)
# Obtener los autovalores asociados a cada componente principal.
autovalores_4 = fit_4.explained_variance_
# Obtener los autovectores asociados a cada componente principal y transponerlos.
autovectores_4 = pd.DataFrame(pca_4.components_.T,
columns = ['Autovector {}'.format(i) for i in range(1, fit_4.n_components_+1)],
index = ['{}_z'.format(variable) for variable in VarExp])
# Obtener la varianza explicada por cada componente principal como un porcentaje de la varianza total.
var_explicada_4 = fit_4.explained_variance_ratio_
# Calcular la varianza explicada acumulada a medida que se agregan cada componente principal.
var_acumulada_4 = np.cumsum(var_explicada_4)
# Crear un DataFrame de pandas con los datos anteriores y establecer índice.
data_4 = {'Autovalores': autovalores_4, 'Variabilidad Explicada': var_explicada_4, 'Variabilidad Acumulada': var_acumulada_4}
tabla_4 = pd.DataFrame(data_4, index=['Componente {}'.format(i) for i in range(1, fit_4.n_components_+1)])
# Imprimir resultados
print(tabla_4)
print(autovectores_4)
Autovalores Variabilidad Explicada Variabilidad Acumulada
Componente 1 1.300857 0.076104 0.076104
Componente 2 1.266597 0.074100 0.150204
Componente 3 1.244398 0.072801 0.223005
Componente 4 1.218301 0.071274 0.294280
Autovector 1 Autovector 2 Autovector 3 \
BodyType_z 0.534740 -0.584109 0.225457
Sex_z -0.002258 0.001354 0.000418
ShowerFreq_z 0.253080 0.646190 0.685253
SocialActivity_z -0.030611 -0.007798 -0.036117
AirTravelFreq_z -0.347710 -0.474512 0.679018
WasteBagSize_z 0.714286 -0.034847 -0.093702
EnergyEfficiency_z 0.013614 0.002156 -0.004618
GroceryBill_z_z 0.059099 0.051092 0.004137
DriveDistance_z_z 0.107611 0.027316 0.057193
WasteBagCount_z_z 0.015255 0.001369 0.014906
TVPCHours_z_z 0.013978 0.038098 -0.011584
ClothesNew_z_z 0.004011 0.050933 0.044176
InternetHours_z_z 0.017886 0.083452 0.045019
RecyclingMetal_z -0.003427 -0.005247 -0.002110
RecyclingPlastic_z 0.002136 -0.004587 -0.000895
RecyclingPaper_z 0.000719 -0.006048 0.000027
RecyclingGlass_z 0.001441 -0.001559 0.001684
CookingWithGrill_z 0.016554 0.004312 0.002683
CookingWithOven_z -0.003443 0.003152 0.009322
CookingWithMicrowave_z 0.002510 0.007220 -0.005394
CookingWithAirfryer_z 0.016554 0.004312 0.002683
CookingWithStove_z 0.009052 0.004287 0.002580
EnergyHeat_electricity_z 0.001471 -0.004259 0.001222
EnergyHeat_natural gas_z 0.007691 0.002332 -0.006722
EnergyHeat_coal_z -0.005565 0.003173 0.001913
EnergyHeat_wood_z -0.003596 -0.001246 0.003587
TransportVehicleType_walk/bicycle_z -0.025526 0.003408 -0.013666
TransportVehicleType_public_z -0.010135 -0.011110 -0.009036
TransportVehicleType_electric_z 0.010317 0.003166 0.002950
TransportVehicleType_hybrid_z 0.006268 0.003326 0.005254
TransportVehicleType_lpg_z 0.008222 -0.000692 0.006090
TransportVehicleType_petrol_z 0.004174 0.001010 0.001340
TransportVehicleType_diesel_z 0.006679 0.000892 0.007068
Diet_pescatarian_z -0.002810 -0.003750 -0.010140
Diet_vegetarian_z 0.005418 -0.001722 -0.000175
Diet_omnivore_z 0.005369 0.000194 0.000393
Diet_vegan_z -0.007977 0.005277 0.009922
Autovector 4
BodyType_z -0.562811
Sex_z -0.005656
ShowerFreq_z -0.154548
SocialActivity_z -0.017157
AirTravelFreq_z 0.433466
WasteBagSize_z 0.672467
EnergyEfficiency_z 0.024487
GroceryBill_z_z 0.085995
DriveDistance_z_z 0.038851
WasteBagCount_z_z 0.074182
TVPCHours_z_z -0.010244
ClothesNew_z_z -0.016547
InternetHours_z_z -0.054262
RecyclingMetal_z 0.002973
RecyclingPlastic_z -0.011337
RecyclingPaper_z -0.004657
RecyclingGlass_z -0.002814
CookingWithGrill_z 0.005745
CookingWithOven_z -0.001766
CookingWithMicrowave_z 0.001940
CookingWithAirfryer_z 0.005745
CookingWithStove_z 0.001122
EnergyHeat_electricity_z -0.001710
EnergyHeat_natural gas_z -0.004550
EnergyHeat_coal_z 0.002363
EnergyHeat_wood_z 0.003897
TransportVehicleType_walk/bicycle_z -0.006513
TransportVehicleType_public_z -0.009305
TransportVehicleType_electric_z 0.002912
TransportVehicleType_hybrid_z 0.002112
TransportVehicleType_lpg_z 0.001664
TransportVehicleType_petrol_z 0.001298
TransportVehicleType_diesel_z 0.007831
Diet_pescatarian_z 0.003151
Diet_vegetarian_z -0.007214
Diet_omnivore_z -0.002079
Diet_vegan_z 0.006143
In [41]:
# Gráfico de la varianza explicada
def plot_varianza_explicada(var_explicada, n_components):
"""
Representa la variabilidad explicada
Args:
var_explicada (array): Un array que contiene el porcentaje de varianza explicada
por cada componente principal. Generalmente calculado como
var_explicada = fit.explained_variance_ratio_ * 100.
n_components (int): El número total de componentes principales.
Generalmente calculado como fit.n_components.
"""
# Crear un rango de números de componentes principales de 1 a n_components
num_componentes_range = np.arange(1, n_components + 1)
# Crear una figura de tamaño 8x6
plt.figure(figsize=(8, 6))
# Trazar la varianza explicada en función del número de componentes principales
plt.plot(num_componentes_range, var_explicada, marker='o')
# Etiquetas de los ejes x e y
plt.xlabel('Número de Componentes Principales')
plt.ylabel('Varianza Explicada')
# Título del gráfico
plt.title('Variabilidad Explicada por Componente Principal')
# Establecer las marcas en el eje x para que coincidan con el número de componentes
plt.xticks(num_componentes_range)
# Mostrar una cuadrícula en el gráfico
plt.grid(True)
# Agregar barras debajo de cada punto para representar el porcentaje de variabilidad explicada
# - 'width': Ancho de las barras de la barra. En este caso, se establece en 0.2 unidades.
# - 'align': Alineación de las barras con respecto a los puntos en el eje x.
# 'center' significa que las barras estarán centradas debajo de los puntos.
# - 'alpha': Transparencia de las barras. Un valor de 0.7 significa que las barras son 70% transparentes.
plt.bar(num_componentes_range, var_explicada, width=0.2, align='center', alpha=0.7)
# Mostrar el gráfico
plt.show()
plot_varianza_explicada(var_explicada_4, fit_4.n_components_)
6. Clustering¶
6.1 Selección del número óptimo de clusters¶
In [42]:
# Calcular las distancias euclidianas por pares
distance_std = distance.cdist(datos_selected, datos_selected,"euclidean")
print(distance_std[:5,:5].round(2))
# Crear un DataFrame a partir de la matriz de distancias
# Se convierte la matriz de distancias en un DataFrame, asignando las mismas etiquetas de fila y columna que el DataFrame original 'datos_selected'.
df_std_distance = pd.DataFrame(distance_std, index = datos_selected.index, columns = datos_selected.index)
[[0. 4.35 5.57 5.23 5.22] [4.35 0. 5.37 5.9 6.52] [5.57 5.37 0. 5.07 6.95] [5.23 5.9 5.07 0. 6.42] [5.22 6.52 6.95 6.42 0. ]]
In [43]:
# Calcular la matriz de enlace para el análisis de clustering jerárquico y crear un dendrograma.
linkage_matrix = sch.linkage(df_std_distance, method='ward')
# Crear el dendrograma utilizando la matriz de enlace calculada
dendrogram = sch.dendrogram(linkage_matrix, labels=datos_selected.index, leaf_font_size=9, leaf_rotation=90)
# Configuración del gráfico
plt.tick_params(axis='x', which='both', bottom=False, top=False, labelbottom=False)
plt.show()
In [44]:
"""
El método del codo (Elbow Method) se utiliza para determinar el número óptimo de clústeres (K) en el análisis de clustering.
El método se basa en encontrar el punto donde la disminución de la variación interna dentro de los clústeres (WCSS) se estabiliza.
"""
# Crear una lista para almacenar los valores de WCSS (Within-Cluster Sum of Squares) para diferentes valores de K
wcss = []
# Iterar sobre un rango de valores para K
for k in range(1, 11):
kmeans = KMeans(n_clusters=k, random_state=0)
kmeans.fit(datos_selected)
wcss.append(kmeans.inertia_) # Almacenar el valor de WCSS (inercia) para el valor actual de K
# Graficar los valores de WCSS en función del número de clústeres (K) para identificar el punto del "codo".
# El "codo" indica el número óptimo de clústeres, donde la disminución de WCSS se estabiliza.
plt.figure(figsize=(8, 6))
plt.plot(range(1, 11), wcss, marker='o', linestyle='-', color='b')
plt.title('Elbow Method')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('WCSS')
plt.grid(True)
plt.show()
In [45]:
"""
El método de las siluetas se utiliza para evaluar la calidad de la agrupación en el clustering.
El puntaje de silueta mide qué tan cerca están los puntos de datos de su propio clúster en comparación con otros clústeres.
Un puntaje más alto indica una mejor agrupación.
"""
# Crear una lista para almacenar los puntajes de silueta para diferentes valores de K
silhouette_scores = []
# Ejecutar el clustering K-means para un rango de valores de K y calcular el puntaje de silueta para cada K
for k in range(2, 11):
kmeans = KMeans(n_clusters=k, random_state=0)
kmeans.fit(datos_selected)
labels = kmeans.labels_
silhouette_avg = silhouette_score(datos_selected, labels)
silhouette_scores.append(silhouette_avg)
# Graficar los puntajes de silueta en función del número de clústeres (K).
# Un puntaje de silueta más alto indica una mejor agrupación.
plt.figure(figsize=(8, 6))
plt.plot(range(2, 11), silhouette_scores, marker='o', linestyle='-', color='b')
plt.title('Silhouette Method')
plt.xlabel('Number of Clusters (K)')
plt.ylabel('Silhouette Score')
plt.grid(True)
plt.show()
6.2 Resultados del clustering con k = 4¶
In [46]:
# Inicializar y ajustar el modelo KMeans con 4 clústeres y una semilla aleatoria para reproducibilidad
kmeans = KMeans(n_clusters=4, random_state=seed)
clusters = kmeans.fit_predict(datos_selected)
# Añadir las etiquetas de clúster al DataFrame 'datos' para análisis futuros
datos['cluster'] = clusters
datos_load['cluster'] = clusters
# Visualizamos la distribución de los puntos de datos entre los 4 clústeres
plt.figure(figsize=(5, 3))
sns.countplot(x='cluster', data=datos_load, palette='Set1')
plt.title('Distribution of Data Points Among Clusters')
plt.xlabel('Cluster')
plt.ylabel('Count')
plt.show()
# Mostrar las primeras filas del DataFrame con las asignaciones de clúster
datos.head()
Out[46]:
| BodyType | Sex | ShowerFreq | SocialActivity | AirTravelFreq | WasteBagSize | EnergyEfficiency | GroceryBill_z | DriveDistance_z | WasteBagCount_z | TVPCHours_z | ClothesNew_z | InternetHours_z | RecyclingMetal | RecyclingPlastic | RecyclingPaper | RecyclingGlass | CookingWithGrill | CookingWithOven | CookingWithMicrowave | CookingWithAirfryer | CookingWithStove | EnergyHeat_electricity | EnergyHeat_natural gas | EnergyHeat_coal | EnergyHeat_wood | TransportVehicleType_walk/bicycle | TransportVehicleType_public | TransportVehicleType_electric | TransportVehicleType_hybrid | TransportVehicleType_lpg | TransportVehicleType_petrol | TransportVehicleType_diesel | Diet_pescatarian | Diet_vegetarian | Diet_omnivore | Diet_vegan | CarbonEmission | cluster | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2.0 | 0 | 2.0 | 2.0 | 2.0 | 2.0 | 0.0 | 0.777025 | -0.657677 | -0.012360 | -0.723218 | 0.060621 | -1.496402 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 2238 | 1 |
| 1 | 3.0 | 0 | 0.0 | 2.0 | 1.0 | 3.0 | 0.0 | -0.828947 | -0.730251 | -0.514803 | -0.441767 | 0.877059 | -0.946714 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1892 | 1 |
| 2 | 2.0 | 1 | 1.0 | 0.0 | 0.0 | 0.0 | 1.0 | -0.496677 | 0.159055 | -1.519689 | 0.261863 | 1.489387 | -0.809292 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 2595 | 3 |
| 3 | 2.0 | 1 | 3.0 | 1.0 | 1.0 | 1.0 | 1.0 | -0.233630 | -0.706782 | -0.514803 | 1.106218 | -1.368146 | -0.671870 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1074 | 3 |
| 4 | 3.0 | 0 | 2.0 | 2.0 | 3.0 | 2.0 | 2.0 | 1.275430 | 2.320034 | -1.519689 | -1.286122 | -1.368146 | -0.809292 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 4743 | 0 |
Investigación de los resultados: Cálculo de medias y desviaciones estándar por grupo
In [47]:
# Calcular las estadísticas descriptivas por cada grupo (media y desviación estándar)
mean_by_cluster = datos.groupby('cluster').mean()
std_by_cluster = datos.groupby('cluster').std()
# Mostrar las medias y desviaciones estándar
display("Medias por cluster:")
display(mean_by_cluster)
display("\nDesviaciones estándar por cluster:")
display(std_by_cluster)
'Medias por cluster:'
| BodyType | Sex | ShowerFreq | SocialActivity | AirTravelFreq | WasteBagSize | EnergyEfficiency | GroceryBill_z | DriveDistance_z | WasteBagCount_z | TVPCHours_z | ClothesNew_z | InternetHours_z | RecyclingMetal | RecyclingPlastic | RecyclingPaper | RecyclingGlass | CookingWithGrill | CookingWithOven | CookingWithMicrowave | CookingWithAirfryer | CookingWithStove | EnergyHeat_electricity | EnergyHeat_natural gas | EnergyHeat_coal | EnergyHeat_wood | TransportVehicleType_walk/bicycle | TransportVehicleType_public | TransportVehicleType_electric | TransportVehicleType_hybrid | TransportVehicleType_lpg | TransportVehicleType_petrol | TransportVehicleType_diesel | Diet_pescatarian | Diet_vegetarian | Diet_omnivore | Diet_vegan | CarbonEmission | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| cluster | ||||||||||||||||||||||||||||||||||||||
| 0 | 1.501441 | 0.482997 | 1.548703 | 0.972911 | 1.454755 | 1.577522 | 1.047262 | 0.044060 | 1.950695 | -0.001935 | 0.005880 | -0.002004 | -0.016442 | 0.480115 | 0.489337 | 0.480115 | 0.486455 | 0.495677 | 0.513545 | 0.503746 | 0.495677 | 0.485303 | 0.250720 | 0.232277 | 0.266282 | 0.250720 | 0.000000 | 0.000000 | 0.208069 | 0.187896 | 0.217867 | 0.197695 | 0.188473 | 0.240346 | 0.234006 | 0.277810 | 0.247839 | 3438.398847 |
| 1 | 2.604938 | 0.503472 | 1.498843 | 0.972608 | 2.221836 | 1.515432 | 0.996528 | -0.034475 | -0.399384 | -0.004413 | -0.015951 | 0.011457 | 0.007393 | 0.495370 | 0.513889 | 0.510417 | 0.489969 | 0.495370 | 0.505401 | 0.500772 | 0.495370 | 0.513117 | 0.259259 | 0.243056 | 0.254630 | 0.243056 | 0.402006 | 0.403935 | 0.035108 | 0.038580 | 0.040509 | 0.039738 | 0.040123 | 0.250772 | 0.258102 | 0.250386 | 0.240741 | 2526.793210 |
| 2 | 0.391973 | 0.494374 | 1.487997 | 0.976744 | 2.221305 | 1.433233 | 1.011253 | 0.003171 | -0.399280 | 0.011952 | -0.001695 | -0.003180 | -0.002389 | 0.520630 | 0.489872 | 0.499625 | 0.504126 | 0.498500 | 0.510128 | 0.513503 | 0.498500 | 0.497374 | 0.261065 | 0.242311 | 0.249062 | 0.247562 | 0.412228 | 0.387847 | 0.039385 | 0.040885 | 0.037509 | 0.041260 | 0.040885 | 0.250938 | 0.231433 | 0.243811 | 0.273818 | 2102.171793 |
| 3 | 1.511473 | 0.509478 | 1.513801 | 1.030928 | 0.312604 | 1.493183 | 0.997340 | 0.001484 | -0.427261 | -0.005676 | 0.011860 | -0.005900 | 0.005232 | 0.512803 | 0.502162 | 0.495178 | 0.505820 | 0.505155 | 0.495178 | 0.509478 | 0.505155 | 0.513136 | 0.249085 | 0.260392 | 0.245095 | 0.245427 | 0.427669 | 0.403392 | 0.037912 | 0.035584 | 0.037912 | 0.030263 | 0.027270 | 0.272032 | 0.254406 | 0.236448 | 0.237113 | 1520.456601 |
'\nDesviaciones estándar por cluster:'
| BodyType | Sex | ShowerFreq | SocialActivity | AirTravelFreq | WasteBagSize | EnergyEfficiency | GroceryBill_z | DriveDistance_z | WasteBagCount_z | TVPCHours_z | ClothesNew_z | InternetHours_z | RecyclingMetal | RecyclingPlastic | RecyclingPaper | RecyclingGlass | CookingWithGrill | CookingWithOven | CookingWithMicrowave | CookingWithAirfryer | CookingWithStove | EnergyHeat_electricity | EnergyHeat_natural gas | EnergyHeat_coal | EnergyHeat_wood | TransportVehicleType_walk/bicycle | TransportVehicleType_public | TransportVehicleType_electric | TransportVehicleType_hybrid | TransportVehicleType_lpg | TransportVehicleType_petrol | TransportVehicleType_diesel | Diet_pescatarian | Diet_vegetarian | Diet_omnivore | Diet_vegan | CarbonEmission | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| cluster | ||||||||||||||||||||||||||||||||||||||
| 0 | 1.094641 | 0.499855 | 1.126291 | 0.820628 | 1.063768 | 1.105017 | 0.810871 | 1.009370 | 0.569765 | 0.996393 | 0.992699 | 1.005900 | 1.002132 | 0.499748 | 0.500030 | 0.499748 | 0.499961 | 0.500125 | 0.499961 | 0.500130 | 0.500125 | 0.499928 | 0.433553 | 0.422406 | 0.442141 | 0.433553 | 0.000000 | 0.000000 | 0.406044 | 0.390742 | 0.412916 | 0.398375 | 0.391202 | 0.427417 | 0.423498 | 0.448048 | 0.431882 | 1274.075396 |
| 1 | 0.488958 | 0.500084 | 1.119973 | 0.818791 | 0.746391 | 1.115032 | 0.812620 | 0.990289 | 0.429545 | 1.000336 | 1.006771 | 0.999361 | 0.995893 | 0.500075 | 0.499904 | 0.499988 | 0.499996 | 0.500075 | 0.500067 | 0.500096 | 0.500075 | 0.499924 | 0.438313 | 0.429011 | 0.435737 | 0.429011 | 0.490398 | 0.490779 | 0.184088 | 0.192630 | 0.197188 | 0.195380 | 0.196287 | 0.433541 | 0.437675 | 0.433319 | 0.427616 | 753.132292 |
| 2 | 0.488282 | 0.500062 | 1.113134 | 0.822881 | 0.741539 | 1.127619 | 0.809341 | 1.004255 | 0.444175 | 0.993832 | 0.999039 | 1.009086 | 1.010929 | 0.499668 | 0.499991 | 0.500094 | 0.500077 | 0.500092 | 0.499991 | 0.499911 | 0.500092 | 0.500087 | 0.439298 | 0.428562 | 0.432551 | 0.431677 | 0.492328 | 0.487351 | 0.194545 | 0.198061 | 0.190042 | 0.198929 | 0.198061 | 0.433634 | 0.421827 | 0.429461 | 0.446001 | 628.814779 |
| 3 | 0.966738 | 0.499993 | 1.116795 | 0.817336 | 0.463632 | 1.124281 | 0.802520 | 0.998705 | 0.384239 | 1.007785 | 0.999666 | 0.989576 | 0.993083 | 0.499919 | 0.500078 | 0.500060 | 0.500049 | 0.500057 | 0.500060 | 0.499993 | 0.500057 | 0.499911 | 0.432555 | 0.438922 | 0.430215 | 0.430412 | 0.494823 | 0.490660 | 0.191014 | 0.185280 | 0.191014 | 0.171338 | 0.162895 | 0.445080 | 0.435599 | 0.424971 | 0.425383 | 487.200021 |
In [48]:
# Seleccionar las columnas a visualizar
variables = datos_load.columns
# Crear un histograma con las frecuencias de la variable, coloreado por clúster
for variable in variables:
plt.figure(figsize=(5, 3))
sns.histplot(data=datos_load, x=variable, hue='cluster', multiple='stack', kde=False, palette='Set1', bins=10)
plt.title(f'Histograma de {variable} por cluster')
plt.xlabel(variable)
plt.ylabel('Frecuencia')
plt.show()
7. Modelos de predicción con Machine Learning¶
In [49]:
X = datos[VarExp] # Seleccionar las características explicativas
y = datos[VarObj] # Seleccionar la variable objetivo
In [50]:
# Investigación de variables más importantes con K Selector
# Normalizar las características usando MinMaxScaler
scaler = MinMaxScaler()
X_transformed = scaler.fit_transform(X)
X_transformed = pd.DataFrame(X_transformed, columns=X.columns, index=X.index)
# Crear el selector K-Best con la función de puntuación chi-cuadrado y seleccionar las 4 mejores características
selector = SelectKBest(score_func=chi2, k=4)
selector.fit(X_transformed,y)
# Crear un DataFrame con los p-valores y las características correspondientes
df_chi2 = pd.DataFrame(zip(selector.pvalues_, X.columns), columns=["pvalor", "feature"]).sort_values("pvalor")
# Graficar los p-valores de las características
plt.figure(figsize=(10, 8))
plt.barh(y=df_chi2["feature"], width=df_chi2["pvalor"])
plt.show()
# Filtrar las características con p-valor menor a 0.05 y mostrar las características seleccionadas
VarsKSelect = df_chi2[df_chi2['pvalor']<0.05]
VarsKSelect = VarsKSelect['feature']
display(VarsKSelect)
31 TransportVehicleType_petrol 30 TransportVehicleType_lpg 32 TransportVehicleType_diesel 29 TransportVehicleType_hybrid Name: feature, dtype: object
7.1 Modelos base: LinearRegression, RandomForest y XGBoost¶
In [51]:
# Dividir el conjunto de datos en entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=seed)
In [52]:
# Función para evaluar el rendimiento del modelo
def evaluate_model(model, X_test, y_test):
"""
Evalúa el rendimiento de un modelo usando las métricas MAE, MSE y R^2.
Inputs:
model: El modelo a evaluar.
X_test: Las características del conjunto de prueba.
y_test: Los valores verdaderos del conjunto de prueba.
Outputs:
mae: Error absoluto medio.
mse: Error cuadrático medio.
r2: Coeficiente de determinación R^2.
"""
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
"""
print(f"Modelo: {model.__class__.__name__}")
print(f"Mean Absolute Error (MAE): {mae:.4f}")
print(f"Mean Squared Error (MSE): {mse:.4f}")
print(f"R^2: {r2:.4f}")
print("-" * 30)
"""
return mae, mse, r2
### 1. Regresión Lineal Múltiple
# Crear el modelo de regresión lineal
linear_model = LinearRegression()
# Entrenar el modelo
linear_model.fit(X_train, y_train)
# Evaluar el modelo
evaluate_model(linear_model, X_test, y_test)
### 2. Random Forest
# Crear el modelo Random Forest
rf_model = RandomForestRegressor(n_estimators=100, random_state=seed)
# Entrenar el modelo
rf_model.fit(X_train, y_train)
# Evaluar el modelo
evaluate_model(rf_model, X_test, y_test)
### 3. XGBoost
# Crear el modelo XGBoost
xgb_model = xgb.XGBRegressor(objective='reg:squarederror', n_estimators=100, random_state=seed)
# Entrenar el modelo
xgb_model.fit(X_train, y_train)
# Evaluar el modelo
evaluate_model(xgb_model, X_test, y_test)
### Comparación de los modelos
linear_mae, linear_mse, linear_r2 = evaluate_model(linear_model, X_test, y_test)
rf_mae, rf_mse, rf_r2 = evaluate_model(rf_model, X_test, y_test)
xgb_mae, xgb_mse, xgb_r2 = evaluate_model(xgb_model, X_test, y_test)
# Resumen de los resultados
resultados = pd.DataFrame({
'Modelo': ['Regresión Lineal', 'Random Forest', 'XGBoost'],
'MAE': [linear_mae, rf_mae, xgb_mae],
'MSE': [linear_mse, rf_mse, xgb_mse],
'R^2': [linear_r2, rf_r2, xgb_r2]
})
print("Comparación de Modelos")
display(resultados)
Comparación de Modelos
| Modelo | MAE | MSE | R^2 | |
|---|---|---|---|---|
| 0 | Regresión Lineal | 210.161687 | 83006.109239 | 0.918714 |
| 1 | Random Forest | 202.184025 | 69587.670517 | 0.931855 |
| 2 | XGBoost | 118.743139 | 26122.756721 | 0.974419 |
In [53]:
# Investigar el sobreajuste
# Realizar predicciones en el conjunto de entrenamiento
y_train_pred = linear_model.predict(X_train)
y_test_pred = linear_model.predict(X_test)
# Calcular el Error Cuadrático Medio (MSE) para el conjunto de entrenamiento
train_mse = mean_squared_error(y_train, y_train_pred)
test_mse = mean_squared_error(y_test, y_test_pred)
# Imprimir los resultados del MSE para ambos conjuntos
print(f"Error de Entrenamiento (MSE): {train_mse}")
print(f"Error de Prueba (MSE): {test_mse}")
Error de Entrenamiento (MSE): 83934.93805871582 Error de Prueba (MSE): 83006.10923925781
7.1.1 Modelo LinearRegression¶
In [54]:
# Añadir constante a las variables independientes (intercepto)
X_train_sm = sm.add_constant(X_train)
X_test_sm = sm.add_constant(X_test)
# Ajustar el modelo de regresión lineal
model = sm.OLS(y_train, X_train_sm)
results = model.fit()
# Resumen completo del modelo (coeficientes, p-valores, R², etc.)
print(results.summary())
# Predecir sobre los datos de prueba
y_pred = results.predict(X_test_sm)
# Calcular métricas de rendimiento
mse = mean_squared_error(y_test, y_pred)
rmse = mean_squared_error(y_test, y_pred, squared=False)
r2 = r2_score(y_test, y_pred)
print(f'MSE: {mse}')
print(f'RMSE: {rmse}')
print(f'R²: {r2}')
OLS Regression Results
==============================================================================
Dep. Variable: CarbonEmission R-squared: 0.919
Model: OLS Adj. R-squared: 0.919
Method: Least Squares F-statistic: 2747.
Date: Tue, 17 Sep 2024 Prob (F-statistic): 0.00
Time: 13:58:49 Log-Likelihood: -56703.
No. Observations: 8000 AIC: 1.135e+05
Df Residuals: 7966 BIC: 1.137e+05
Df Model: 33
Covariance Type: nonrobust
=====================================================================================================
coef std err t P>|t| [0.025 0.975]
-----------------------------------------------------------------------------------------------------
const 667.9423 9.303 71.797 0.000 649.706 686.179
BodyType 184.1589 2.900 63.497 0.000 178.474 189.844
Sex 343.1866 6.504 52.762 0.000 330.436 355.937
ShowerFreq 5.8481 2.905 2.013 0.044 0.154 11.542
SocialActivity 87.4742 3.961 22.087 0.000 79.711 95.238
AirTravelFreq 437.2734 2.916 149.940 0.000 431.557 442.990
WasteBagSize 129.0740 2.905 44.426 0.000 123.379 134.769
EnergyEfficiency -29.5479 4.015 -7.360 0.000 -37.418 -21.678
GroceryBill_z 65.1765 3.249 20.060 0.000 58.807 71.545
DriveDistance_z 568.7748 5.355 106.222 0.000 558.278 579.271
WasteBagCount_z 162.2490 3.242 50.047 0.000 155.894 168.604
TVPCHours_z 21.4405 3.256 6.585 0.000 15.058 27.823
ClothesNew_z 201.3640 3.238 62.192 0.000 195.017 207.711
InternetHours_z 47.7816 3.247 14.716 0.000 41.417 54.147
RecyclingMetal -128.8699 6.506 -19.808 0.000 -141.623 -116.117
RecyclingPlastic -61.5361 6.506 -9.458 0.000 -74.290 -48.782
RecyclingPaper -148.8054 6.505 -22.877 0.000 -161.556 -136.055
RecyclingGlass -91.9277 6.501 -14.141 0.000 -104.671 -79.185
CookingWithGrill 19.0061 3.250 5.847 0.000 12.634 25.378
CookingWithOven 34.5549 6.505 5.312 0.000 21.804 47.306
CookingWithMicrowave 14.3593 6.503 2.208 0.027 1.613 27.106
CookingWithAirfryer 19.0061 3.250 5.847 0.000 12.634 25.378
CookingWithStove 27.2889 6.505 4.195 0.000 14.537 40.041
EnergyHeat_electricity -57.9078 6.027 -9.608 0.000 -69.723 -46.093
EnergyHeat_natural gas 170.1675 6.124 27.785 0.000 158.162 182.173
EnergyHeat_coal 375.4052 6.062 61.925 0.000 363.522 387.289
EnergyHeat_wood 180.2774 6.159 29.270 0.000 168.204 192.351
TransportVehicleType_walk/bicycle 91.8593 9.348 9.826 0.000 73.534 110.184
TransportVehicleType_public -39.8209 8.149 -4.887 0.000 -55.795 -23.847
TransportVehicleType_electric -955.4191 11.942 -80.006 0.000 -978.828 -932.010
TransportVehicleType_hybrid -120.3022 12.013 -10.014 0.000 -143.852 -96.753
TransportVehicleType_lpg 444.1395 11.826 37.556 0.000 420.957 467.322
TransportVehicleType_petrol 931.5764 12.044 77.345 0.000 907.966 955.187
TransportVehicleType_diesel 315.9093 12.292 25.701 0.000 291.814 340.004
Diet_pescatarian 168.8966 5.995 28.175 0.000 157.145 180.648
Diet_vegetarian 130.3279 6.123 21.286 0.000 118.326 142.330
Diet_omnivore 264.6706 6.104 43.364 0.000 252.706 276.635
Diet_vegan 104.0472 6.159 16.893 0.000 91.974 116.121
==============================================================================
Omnibus: 823.262 Durbin-Watson: 1.963
Prob(Omnibus): 0.000 Jarque-Bera (JB): 3901.285
Skew: 0.398 Prob(JB): 0.00
Kurtosis: 6.327 Cond. No. 1.06e+16
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The smallest eigenvalue is 1.14e-27. This might indicate that there are
strong multicollinearity problems or that the design matrix is singular.
MSE: 82982.41783628282
RMSE: 288.0666899110045
R²: 0.9187375155131745
7.1.2 Modelo RandomForest¶
In [55]:
# Obtener las importancias de las variables
importances = rf_model.feature_importances_
# Crear un DataFrame con las importancias de las variables
importance_df = pd.DataFrame({
'Feature': X.columns,
'Importance': importances
}).sort_values(by='Importance', ascending=False)
# Visualizar las importancias de las variables
plt.figure(figsize=(12, 8))
sns.barplot(x='Importance', y='Feature', data=importance_df, palette='viridis')
plt.title('Importancia de las variables en el modelo de Random Forest')
plt.xlabel('Importancia')
plt.ylabel('Características')
plt.show()
7.1.3 Modelo XGBoost¶
In [56]:
# Crear el modelo XGBRegressor
model = xgb.XGBRegressor(objective='reg:squarederror', random_state=seed)
# Definir los hiperparámetros a ajustar
param_grid = {
'n_estimators': [50, 100, 150, 200],
'max_depth': [3, 4, 5, 6],
'learning_rate': [0.01, 0.1, 0.2],
'min_child_weight': [1, 3, 5],
'gamma': [0, 0.1, 0.2],
'subsample': [0.8, 0.9, 1.0],
'colsample_bytree': [0.8, 0.9, 1.0]
}
# Crear el GridSearchCV
grid_search = GridSearchCV(estimator=model,
param_grid=param_grid,
scoring=make_scorer(mean_squared_error, greater_is_better=False),
cv=5,
verbose=0,
n_jobs=-1)
# Ajustar el modelo
grid_search.fit(X_train, y_train)
# Obtener los mejores parámetros
print("Mejores parámetros encontrados:")
print(grid_search.best_params_)
# Evaluar el modelo con los mejores parámetros
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
# Calcular métricas de rendimiento
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"Mean Squared Error: {mse}")
print(f"R^2 Score: {r2}")
Mejores parámetros encontrados:
{'colsample_bytree': 1.0, 'gamma': 0, 'learning_rate': 0.2, 'max_depth': 3, 'min_child_weight': 1, 'n_estimators': 200, 'subsample': 0.9}
Mean Squared Error: 15604.975343731325
R^2 Score: 0.9847184419631958
In [57]:
# Evaluar el modelo con los mejores parámetros
best_model = grid_search.best_estimator_
y_pred = best_model.predict(X_test)
# Calcular métricas de rendimiento
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"MAE: {mae}")
print(f"Mean Squared Error: {mse}")
print(f"R^2 Score: {r2}")
MAE: 92.33867385864258 Mean Squared Error: 15604.975343731325 R^2 Score: 0.9847184419631958
In [58]:
# Comprobar el sobreajuste
# Predecir sobre los datos de prueba y entrenamiento
y_train_pred = best_model.predict(X_train)
y_test_pred = best_model.predict(X_test)
# Calcular errores
train_mse = mean_squared_error(y_train, y_train_pred)
test_mse = mean_squared_error(y_test, y_test_pred)
print(f"Error de Entrenamiento (MSE): {train_mse}")
print(f"Error de Prueba (MSE): {test_mse}")
Error de Entrenamiento (MSE): 9665.952624541022 Error de Prueba (MSE): 15604.975343731325
In [59]:
# Graficar la curva de aprendizaje
# Calcular el tamaño del conjunto de entrenamiento, los puntajes de entrenamiento y prueba
train_sizes, train_scores, test_scores = learning_curve(best_model, X, y, cv=5, scoring='neg_mean_squared_error', n_jobs=-1)
# Calcular el promedio y desviación estándar
train_mean = -train_scores.mean(axis=1)
test_mean = -test_scores.mean(axis=1)
train_std = train_scores.std(axis=1)
test_std = test_scores.std(axis=1)
# Configurar la gráfica
plt.figure()
plt.plot(train_sizes, train_mean, 'o-', color='r', label='Error de Entrenamiento')
plt.plot(train_sizes, test_mean, 'o-', color='g', label='Error de Prueba')
plt.fill_between(train_sizes, train_mean - train_std, train_mean + train_std, alpha=0.1, color='r')
plt.fill_between(train_sizes, test_mean - test_std, test_mean + test_std, alpha=0.1, color='g')
plt.title('Curvas de Aprendizaje')
plt.xlabel('Tamaño del Conjunto de Entrenamiento')
plt.ylabel('Error Cuadrático Medio')
plt.legend(loc='best')
plt.grid()
plt.show()
7.2 Productivizar el modelo¶
In [60]:
# Guardar el modelo predictivo entrenado en un archivo
joblib.dump(best_model, 'modelo_entrenado.pkl')
Out[60]:
['modelo_entrenado.pkl']
In [61]:
# Guardar el modelo clustering entrenado en un archivo
joblib.dump(kmeans, 'modelo_clustering_entrenado.pkl')
Out[61]:
['modelo_clustering_entrenado.pkl']